module width_convert(
	input				clk,
	input				rst_n,
	input				enable,
	input	[2:0]		width_type,
	input	[2:0]		read_width_type,

	input	[95:0]		data_in,

	input				read_req_sha3,

	output 	reg			read_req,
	output 	reg 		valid_o,
	output	reg [63:0]	data_out	
	);
	
//*****************************************
//	width_type:
//				3'd1:	32
// 				3'd2:	40
// 				3'd3:	64
// 				3'd4:	80
// 				3'd5:	96
//				3'd6:	88//巨恶心
	reg			read_en;
	reg			read_en_buffer;
	reg			read_req_buffer;
	// reg [3:0]	read_req_sha3_buffer;
	reg [3:0]	index;
	reg [143:0]	buffer;
	reg [9:0]	record;
	reg	[7:0]	remaining;
	reg [4:0]	write_cnt;
	reg 		stop_write;
	reg 		stop_read;
	wire 		flag;
	wire 		flag1;
	reg 		flag_buffer;
	reg [2:0]	width_type_buffer,width_type_buffer1;

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			width_type_buffer1 <= 3'd0;
		else
			width_type_buffer1 <= read_width_type;
	end

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			width_type_buffer <= 3'd0;
		else if(stop_write)
			width_type_buffer <= width_type_buffer;
		else
			width_type_buffer <= width_type_buffer1;
	end


	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			flag_buffer <= 1'b0;
		else
			flag_buffer <= flag;
	end	

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			stop_read <= 1'b0;
		else if(~enable)
			stop_read <= 1'b1;
		else if(read_req_sha3)
			stop_read <= 1'b0;
		else if(flag_buffer)
			stop_read <= 1'b1;
		else
			stop_read <= stop_read;
	end

	assign	flag = write_cnt == 5'd16 && record != 10'd0 &&	(width_type == 3'd1 ? record < 10'd32 :
										 					 width_type == 3'd2 ? record < 10'd40 :
										 					 width_type == 3'd3 ? record < 10'd64 :
										 					 width_type == 3'd4 ? record < 10'd80 :
										 					 width_type == 3'd5 ? record < 10'd96 :
										 					 width_type == 3'd6 ? record < 10'd88 : 1'b0);
	assign 	flag1= write_cnt == 5'd16 && valid_o;


	// always@(posedge clk or negedge rst_n)
	// begin
	// 	if(!rst_n)
	// 		read_req <= 1'b0;
	// 	else
	// 	begin
	// 		case(width_type)
	// 			3'd1:	read_req <= (record > 8'd31) | (flag); 
	// 			3'd2:	read_req <= (record > 8'd39) | (flag);
	// 			3'd3:	read_req <= (record > 8'd63) | (flag);
	// 			3'd4:	read_req <= (record > 8'd79) | (flag);
	// 			3'd5:	read_req <= (record > 8'd95) | (flag);
	// 			3'd6:	read_req <= (record > 8'd87) | (flag);
	// 			default:read_req <= 1'b0;
	// 		endcase // width_type			
	// 	end
	// end

	always@(*)
	begin
		if(~enable)
			read_req = 1'b0;
		else
			case(read_width_type)
				3'd1:	read_req = stop_read ? 1'b0 : (record > 10'd31) | (flag_buffer); 
				3'd2:	read_req = stop_read ? 1'b0 : (record > 10'd39) | (flag_buffer);
				3'd3:	read_req = stop_read ? 1'b0 : (record > 10'd63) | (flag_buffer);
				3'd4:	read_req = stop_read ? 1'b0 : (record > 10'd79) | (flag_buffer);
				3'd5:	read_req = stop_read ? 1'b0 : (record > 10'd95) | (flag_buffer);
				3'd6:	read_req = stop_read ? 1'b0 : (record > 10'd64 && record < 10'd512) | (flag_buffer);
				default:read_req = 1'b0;
			endcase // width_type			
	end

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			buffer <= 144'b0;
		else if(~enable)
			buffer <= 144'b0;
		else if(read_en)
		begin
			case(read_width_type)
				3'd1:	buffer[127:0] <= {data_in[12*7+3:12*7+0],data_in[12*6+3:12*6+0],data_in[12*5+3:12*5+0],data_in[12*4+3:12*4+0],
								   		  data_in[12*3+3:12*3+0],data_in[12*2+3:12*2+0],data_in[12*1+3:12*1+0],data_in[12*0+3:12*0+0],buffer[127:32]};
				3'd2:	buffer[127:0] <= {data_in[12*7+4:12*7+0],data_in[12*6+4:12*6+0],data_in[12*5+4:12*5+0],data_in[12*4+4:12*4+0],
								   		  data_in[12*3+4:12*3+0],data_in[12*2+4:12*2+0],data_in[12*1+4:12*1+0],data_in[12*0+4:12*0+0],buffer[127:40]};
				3'd3:	buffer[127:0] <= {data_in[63:0],buffer[127:64]};
				3'd4:	buffer[127:0] <= {data_in[12*7+9:12*7+0],data_in[12*6+9:12*6+0],data_in[12*5+9:12*5+0],data_in[12*4+9:12*4+0],
								   		  data_in[12*3+9:12*3+0],data_in[12*2+9:12*2+0],data_in[12*1+9:12*1+0],data_in[12*0+9:12*0+0],buffer[127:80]};
				3'd5:	buffer[127:0] <= {data_in,buffer[127:96]};
				3'd6:	buffer 		  <= {data_in[12*7+10:12*7+0],data_in[12*6+10:12*6+0],data_in[12*5+10:12*5+0],data_in[12*4+10:12*4+0],
								   		  data_in[12*3+10:12*3+0],data_in[12*2+10:12*2+0],data_in[12*1+10:12*1+0],data_in[12*0+10:12*0+0],buffer[143:88]};
				default:buffer <= buffer;
			endcase // width_type
		end
		else
			buffer <= buffer;
	end

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			index <= 4'b0;
		else if(~enable)
			index <= 4'b0;
		else if(read_en_buffer)
		begin
			case(width_type_buffer)
				3'd1:	index <= index == 4'd1  ? 4'd0 : index + 1'b1;
				3'd2:	index <= index == 4'd7  ? 4'd0 : index + 1'b1;
				3'd3:	index <= 4'd0; 
				3'd4:	index <= index == 4'd4  ? 4'd0 : index + 1'b1;
				3'd5:	index <= index == 4'd2  ? 4'd0 : index + 1'b1;
				3'd6:	index <= index == 4'd10 ? 4'd0 : index + 1'b1;
				default:index <= index + 1'b1;
			endcase // width_type
		end
		else if(remaining > 8'd63 && ~(stop_write | flag1))
		begin
			case(width_type_buffer)
				3'd1:	index <= index == 4'd1  ? 4'd0 : index;
				3'd2:	index <= index == 4'd7  ? 4'd0 : index;
				3'd3:	index <= 4'd0; 
				3'd4:	index <= index == 4'd4  ? 4'd0 : index;
				3'd5:	index <= index == 4'd2  ? 4'd0 : index;
				3'd6:	index <= (index == 3'd3 || index == 3'd7) ? index + 1'b1 : index == 4'd10 ? 4'd0 : index;
				default:index <= index;
			endcase // width_type
		end	
		else
		index <= index;
	end


	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			data_out <= 64'b0;
		else if(~enable)
			data_out <= 64'b0;
		else
		begin
			case(width_type_buffer)
				3'd1:	begin
							case(index)
								4'd1:	data_out <= buffer[127:64];
								default:data_out <= 64'b0;
							endcase // index 
						end
				3'd2:	begin
							case(index)
								4'd1:	data_out <= buffer[111:48];
								4'd3:	data_out <= buffer[95:32];
								4'd4:	data_out <= buffer[119:56];
								4'd6:	data_out <= buffer[103:40];
								4'd7:	data_out <= buffer[127:64];
								default:data_out <= 64'b0;
							endcase // index
						end
				3'd3:	data_out <= buffer[127:64];
				3'd4:	begin
							case(index)
								4'd0:	data_out <= buffer[111:48];
								4'd1:	data_out <= buffer[95:32];
								4'd2:	data_out <= buffer[79:16];
								4'd3:	data_out <= buffer[63:0];
								4'd4:	data_out <= buffer[127:64];
								default:data_out <= 64'b0;
							endcase // index
						end
				3'd5:	begin
							case(index)
								4'd0:	data_out <= buffer[95:32];
								4'd1:	data_out <= buffer[63:0];
								4'd2:	data_out <= buffer[127:64];
								default:data_out <= 64'b0;
							endcase // index
						end
				3'd6:	begin
							case(index)
								4'd0:	data_out <= buffer[119:56];
								4'd1:	data_out <= buffer[95:32];
								4'd2:	data_out <= buffer[71:8];
								4'd3:	data_out <= buffer[135:72];
								4'd4:	data_out <= buffer[111:48];
								4'd5:	data_out <= buffer[87:24];
								4'd6:	data_out <= buffer[63:0];
								4'd7:	data_out <= buffer[127:64];
								4'd8:	data_out <= buffer[103:40];
								4'd9:	data_out <= buffer[79:16];
								4'd10:	data_out <= buffer[143:80];
								default:data_out <= 64'b0;
							endcase // index
						end
				default:data_out <= 64'b0;
			endcase // index
		end
	end

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			valid_o <= 1'b0;
		else if(stop_write | flag1)
			valid_o <= 1'b0;
		else
			valid_o <= remaining > 8'd63;
	end				
//**********************************************************************************************
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			read_req_buffer <= 1'b0;
		else
			read_req_buffer <= read_req;
	end

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			read_en <= 1'b0;
		else
			read_en <= read_req_buffer;
	end

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			read_en_buffer <= 1'b0;
		else
			read_en_buffer <= read_en;
	end

	// always@(posedge clk or negedge rst_n)
	// begin
	// 	if(!rst_n)
	// 		read_req_sha3_buffer <= 4'b0;
	// 	else if(~enable)
	// 		read_req_sha3_buffer <= 4'b0;
	// 	else
	// 		read_req_sha3_buffer <= {read_req_sha3,read_req_sha3_buffer[3:1]};
	// end
//**********************************************************************************************
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			record <= 10'd0;
		else if(~enable)
			record <= 10'd0;
		else if(flag1)
			record <= 10'd0 - remaining;
		else
		begin
			case(width_type)
				3'd1:	record <= record + ((read_req_sha3 | (~stop_read && flag)) ? 10'd64 : 10'd0) - (read_req ? 10'd32 : 10'd0);
				3'd2:	record <= record + ((read_req_sha3 | (~stop_read && flag)) ? 10'd64 : 10'd0) - (read_req ? 10'd40 : 10'd0);
				3'd3:	record <= record + ((read_req_sha3 | (~stop_read && flag)) ? 10'd64 : 10'd0) - (read_req ? 10'd64 : 10'd0);
				3'd4:	record <= record + ((read_req_sha3 | (~stop_read && flag)) ? 10'd64 : 10'd0) - (read_req ? 10'd80 : 10'd0);
				3'd5:	record <= record + ((read_req_sha3 | (~stop_read && flag)) ? 10'd64 : 10'd0) - (read_req ? 10'd96 : 10'd0);
				3'd6:	record <= record + ((read_req_sha3 | (~stop_read && flag)) ? 10'd64 : 10'd0) - (read_req ? 10'd88 : 10'd0);
				default:record <= record;
			endcase // {read_req_sha3,read_req}
		end
	end

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			remaining <= 8'd0;
		else if(~enable)
			remaining <= 8'd0;
		else
		begin
			case(read_width_type)
				3'd1:	remaining <= remaining - ((remaining > 8'd63 && ~(stop_write | flag1)) ? 8'd64 : 8'd0) + (read_en ? 8'd32 : 8'd0);
				3'd2:	remaining <= remaining - ((remaining > 8'd63 && ~(stop_write | flag1)) ? 8'd64 : 8'd0) + (read_en ? 8'd40 : 8'd0);
				3'd3:	remaining <= remaining - ((remaining > 8'd63 && ~(stop_write | flag1)) ? 8'd64 : 8'd0) + (read_en ? 8'd64 : 8'd0);
				3'd4:	remaining <= remaining - ((remaining > 8'd63 && ~(stop_write | flag1)) ? 8'd64 : 8'd0) + (read_en ? 8'd80 : 8'd0);
				3'd5:	remaining <= remaining - ((remaining > 8'd63 && ~(stop_write | flag1)) ? 8'd64 : 8'd0) + (read_en ? 8'd96 : 8'd0);
				3'd6:	remaining <= remaining - ((remaining > 8'd63 && ~(stop_write | flag1)) ? 8'd64 : 8'd0) + (read_en ? 8'd88 : 8'd0);
				default:remaining <= remaining;
			endcase // {read_req_sha3,read_req}
		end
	end
//**********************************************************************************************
	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			write_cnt <= 5'b0;
		else if(~enable)
			write_cnt <= 5'b0;
		else if(write_cnt == 5'd17)
			write_cnt <= 5'd0;
		else if(valid_o)
			write_cnt <= write_cnt + 1'b1;
		else
			write_cnt <= write_cnt;
	end

	always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			stop_write <= 1'b0;
		else if(~enable)
			stop_write <= 1'b0;
		else if(read_req_sha3)
			stop_write <= 1'b0;
		else if(flag1)
			stop_write <= 1'b1;
		else
			stop_write <= stop_write;
	end

endmodule